package kernel.loaders.ascii;

import kernel.loaders.*;
import java.io.*;

/**
 *
 * @author George
 */
public class ASCIIScanner extends STLScanner {

    private char currentChar;
    private byte currentKind;
    private StringBuffer currentSpelling;
    private SourceSTLFile sourceFile;

    public ASCIIScanner(SourceSTLFile sourceFile) {
        this.sourceFile = sourceFile;
        this.currentChar = this.sourceFile.getSource();
        this.currentSpelling = new StringBuffer();
    }

    public SourceSTLFile getSourceSTLFile() {
        return this.sourceFile;
    }

    public boolean hasMoreTokens() {
        if ((int) currentChar == 0) {
            return true;
        } else {
            return false;
        }
    }

    private void takeIt() {
        currentSpelling.append(currentChar);
        currentChar = this.sourceFile.getSource();
    }

    private void take(char expectedChar) {
        if (currentChar == expectedChar) {
            currentSpelling.append(currentChar);
            currentChar = this.sourceFile.getSource();
        }
    }

    private boolean isLetter(char c) {
        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
            return true;
        } else {
            return false;
        }
    }

    private boolean isDigit(char c) {
        if (c >= '0' && c <= '9') {
            return true;
        } else {
            return false;
        }
    }

    private boolean isDot(char c) {
        if (c == '.') {
            return true;
        } else {
            return false;
        }
    }

    public Token scan() throws IOException {
        try {
            while (currentChar == ('!') || currentChar == (' ') || currentChar == ('\r') || currentChar == ('\n')) {
                scanSeparator();
            }
            if (this.hasMoreTokens())
              return null;
            
            currentSpelling = new StringBuffer("");
            currentKind = scanToken();
            Token token = new Token(currentKind, currentSpelling.toString());
            return (token);
        } catch (InvalidTokenException invalidToken) {
            IOException ioException = new IOException();
            throw ioException;
        }
    }

    private void scanSeparator() {
        switch (currentChar) {
            case '!':
                 {
                    takeIt();
                    take('\r'); //eol                

                }
                break;
            case ' ':
            case '\r':
                takeIt(); //espaço | eol
                break;
        }
    }

    private boolean isBar(char c) {
        if (c == '\'') {
            return true;
        } else {
            return false;
        }
    }

    private boolean isBacksLash(char c) {
        if (c == '/') {
            return true;
        } else {
            return false;
        }
    }

    private boolean isMinus(char c) {
        if (c == '-') {
            return true;
        } else {
            return false;
        }
    }

    private boolean isPlus(char c) {
        if (c == '+') {
            return true;
        } else {
            return false;
        }
    }

    private boolean isAllowedCharForIdentifier(char c, boolean includeFristPosition) {
        if (!includeFristPosition && isDigit(c)) {
            return true;
        }
        if (isLetter(c) || isDot(c) || isBar(c) || isBacksLash(c)) {
            return true;
        } else {
            return false;
        }
    }

    private boolean isAllowedCharForFloat(char c, boolean includeFirstPosition) {
        if (includeFirstPosition && (isMinus(c) || isPlus(c))) {
            return true;
        } else if (isDigit(c)) {
            return true;
        } else {
            return false;
        }
    }

    private byte scanToken() throws InvalidTokenException {
        if (isAllowedCharForIdentifier(currentChar, true)) {
            this.takeIt();
            while (isAllowedCharForIdentifier(currentChar, false)) {
                this.takeIt();
            }
            return Token.IDENTIFIER;
        } else if (isAllowedCharForFloat(currentChar, true)) {
            this.takeIt();
            while (isDigit(currentChar)) {
                this.takeIt();
            }
            if (currentChar == '.') {
                this.takeIt();
                while (isDigit(currentChar)) {
                    this.takeIt();
                }
            }
            if (currentChar == 'E' || currentChar == 'e') {
                this.takeIt();
                if (isMinus(currentChar) || isPlus(currentChar))
                    this.takeIt();
                while (isDigit(currentChar))
                    this.takeIt();
            }
            return Token.FLOAT;
        }
        InvalidTokenException exception = new InvalidTokenException();
        throw exception;
    }
}
